﻿ using System;
using System.Xml;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Collections.ObjectModel;

namespace Postcode.Settings
{
    public class PostcodeProperty
    {
        #region Private variables
        private string _isoName;
        private string _loose;
        private string _strict;
        private string _englishName;
        private string _nativeName;
        private string _prefix;
        private string _format;
        private List<string> _postcodeParts = new List<string>();
        #endregion
        #region Properties
        public string ISOName { get { return this._isoName; } }
        public Regex Loose { get { return new Regex(this._loose); } }
        public Regex Strict { get { return new Regex(this._strict); } }
        public string EnglishName { get { return this._englishName; } }
        public string NativeName { get { return this._nativeName; } }
        public string Prefix { get { return this._prefix; } }
        public string Format { get { return this._format; } }
        /// <summary>
        /// Multiple regular expression to add spaces to make the library able to 
        /// transform the postcode to a strict format
        /// 
        /// ! Be aware that this doesn't always return valid regular expressions you need 
        ///   to catch invalid expressions
        /// </summary>
        public ReadOnlyCollection<string> PostcodeParts
        {
            get 
            {
                try
                {
                    // if no parts have been specified but the library can detect spaces in the regular
                    // expression it will break up the regular expression to try and add spaces through
                    // that method, if that fails the original postcode can always be returned
                    if (this._postcodeParts.Count == 0 && this._strict.Contains("\\ "))
                    {
                        string[] parts = this._strict.Split(new char[] { '\\', ' ' });
                        string tmp = "";
                        foreach (string part in parts)
                        {
                            tmp += (!String.IsNullOrEmpty(part)) ? part : "\\ ";
                            if (!tmp.EndsWith(" ")) { this._postcodeParts.Add(tmp); }
                        }
                    }
                }
                catch { }
                return this._postcodeParts.AsReadOnly();
            }
        }
        #endregion

        /// <summary>
        /// Constructor that accepts a XmlReader to load the data
        /// </summary>
        /// <param name="reader">Reader should contain subtree matching the required fields</param>
        public PostcodeProperty(XmlReader reader)
        {
            this.load(reader);
        }

        /// <summary>
        /// Load the PostcodeProperty object using all given variables
        /// </summary>
        /// <param name="ISOName">Two letter ISO name</param>
        /// <param name="Native_Name">Native name of the country</param>
        /// <param name="English_Name">English name for the country</param>
        /// <param name="Format">The basic postcode format</param>
        /// <param name="Strict">The strict postcode format regex</param>
        /// <param name="Loose">The loose postcode format regex</param>
        /// <param name="Prefix">The prefix that can lead the format</param>
        public PostcodeProperty(string ISOName,
                                string Native_Name,
                                string English_Name,
                                string Format,
                                string Strict,
                                string Loose,
                                string Prefix)
        {
            this._isoName = ISOName;
            this._nativeName = Native_Name;
            this._englishName = English_Name;
            this._format = Format;
            this._strict = Strict;
            this._loose = Loose;
            this._prefix = Prefix;
        }

        /// <summary>
        /// Find key value pars and map them to the object
        /// </summary>
        /// <param name="reader"></param>
        private void load(XmlReader reader)
        {
            String key = "";
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        if (reader.HasAttributes)
                        {
                            loadAttributes(reader);
                        }
                        key = reader.Name;
                        break;
                    case XmlNodeType.Text:
                        this.map(key, reader.Value);
                        break;
                }
            }
        }

        /// <summary>
        /// Loads all attributes on an element and tries to match them to attributes of the PostcodeProperty object
        /// </summary>
        /// <param name="reader">the XmlReader at the position of an element</param>
        private void loadAttributes(XmlReader reader)
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                while (reader.MoveToNextAttribute())
                {
                    this.map(reader.Name, reader.Value);
                }
                reader.MoveToElement();
            }
        }

        /// <summary>
        /// Map the key value pairs to the objects private variables
        /// </summary>
        /// <param name="key">Name of the xml node</param>
        /// <param name="value">Value of the xml node</param>
        private void map(String key, String value)
        {
            switch (key)
            {
                case "ISOCode":
                    this._isoName = value;
                    break;
                case "Native_Name":
                    this._nativeName = value;
                    break;
                case "English_Name":
                    this._englishName = value;
                    break;
                case "Format":
                    this._format = value;
                    break;
                case "Strict":
                    this._strict = value;
                    break;
                case "Loose":
                    this._loose = value;
                    break;
                case "Prefix":
                    this._prefix = value;
                    break;
                case "Part":
                    this._postcodeParts.Add(value);
                    break;
                default: 
                    break;
            }
        }
    }
}
